/*
 * MIT License
 *
 * Copyright (c) 2020 wen.gu <454727014@qq.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

 /***************************************************************************
 * Name: dog_micro_service_container.cpp
 *
 * Purpose: a service group by many micro services implementation.
 *
 * Developer:
 *   wen.gu , 2020-08-20
 *
 * TODO:
 *
 ***************************************************************************/

 /******************************************************************************
 **    INCLUDES
 ******************************************************************************/
#define LOG_TAG "MSVC"
#include "service/dog_micro_service_container.h"

#include "service/dog_micro_service.h"
#include "dog_plugin_manager.h"

#include "dog_log.h"
namespace dog
{

/******************************************************************************
 **    MACROS
 ******************************************************************************/

/******************************************************************************
 **    VARIABLE DEFINITIONS
 ******************************************************************************/

class MicroServiceContainer::impl
{
public:
    using MicroServiceMap = std::map<std::string, MicroService*>;
public:
    MicroServiceMap mMicroSrvs;
};

/******************************************************************************
 **    FUNCTION DEFINITIONS
 ******************************************************************************/
MicroServiceContainer::MicroServiceContainer()
    :mImpl(new impl)
{
    /** todo something */
}

MicroServiceContainer::~MicroServiceContainer()
{
    /** todo something */
    clear();
}

/**
    *  [in]  serviceName: the name of service
    *  [in]  comType:  the type of communication(e.g. dbus, cm an so on)
    */
DogErrc MicroServiceContainer::initialize(const std::string& serviceName, const std::string& comType)
{
    /** todo something */
    return Service::initialize(serviceName, comType);
}

DogErrc MicroServiceContainer::start()
{
    for (auto it : mImpl->mMicroSrvs)
    {
        MicroService* microSrv = it.second;

        if (microSrv)
        {
            microSrv->start();
        }
    }

    return Service::start();
}

DogErrc MicroServiceContainer::stop()
{
    DogErrc ret = Service::stop();

    for (auto it : mImpl->mMicroSrvs)
    {
        MicroService* microSrv = it.second;

        if (microSrv)
        {
            microSrv->stop();
        }
    }

    return ret;
}


/** the life cycle of current micro service will be trusteeship by container(e.g. delete ) */
bool MicroServiceContainer::registerMicroService(MicroService* srv)
{
    bool ret = false;
    if (srv)
    {
        const std::string& srvName = srv->name();
        impl::MicroServiceMap::iterator it = mImpl->mMicroSrvs.find(srvName);

        if (it == mImpl->mMicroSrvs.end())
        {
            srv->registerMehotds(getMethodMap());
            srv->setMethodHandler([this](const std::string& methodName, const Value& param, Value& reply) -> DogErrc
            {
                return doMethod(methodName, param, reply);
            });

            srv->setSignalHandler([this](const std::string& sigName, const Value& val)
            {
                emitSignal(sigName, val);
            });

            mImpl->mMicroSrvs[srvName] = srv;
            ret = true;
        }
        else
        {
            LOGE("micro srv(%s) is exist, register before or two srv have same name\n", srvName.c_str());
        }
    }

    return ret;
}


/** auto load micro service component with plugin manager */
bool MicroServiceContainer::registerMicroService(const std::string& srvName)
{
    bool res = false;
    PluginManager& pm = PluginManager::getInstance();

    MicroSrvCreateParam param;
    param.name = srvName;

    MicroService* srv = nullptr;
    DogErrc ret = pm.find(&param, (Component**)&srv);

    if (DogErrc::OK == ret)
    {
        res = registerMicroService(srv);
    }
    else
    {
        LOGE("load micro service(%s) plugin failed(%d, %s)\n",  srvName.c_str(), ret, ErrorStr(ret));
    }

    return res;
}

/** auto load micro services component with plugin manager */
bool MicroServiceContainer::registerMicroService(const std::vector<std::string>& srvNames, bool ignoreError /*= true*/)
{
    bool ret = false;
    uint32_t cnt = 0;
    for (size_t i = 0; i < srvNames.size(); i++)
    {
        ret = registerMicroService(srvNames[i]);

        if (ret)
        {
            cnt++;
        }
        else if (ignoreError)
        {
            ret = true;
        }
        else
        {
            LOGE("register srv(%s)failed\n", srvNames[i].c_str());
            break;
        }
    }

    LOGD("request register srv count(%d), success count(%d)\n",  srvNames.size(), cnt);

    return ret;
}

/** unregister and release all micro service in current container */
void MicroServiceContainer::clear() /** only when stop state can clear micro services */
{
    if (!isRunning())
    {
        for (auto it : mImpl->mMicroSrvs)
        {
            MicroService* srv = it.second;

            if (srv)
            {
                delete srv;
            }
        }

        mImpl->mMicroSrvs.clear();
    }
    else
    {
        LOGE("do this only in stop state, now is running.\n");
    }
}

MicroService* MicroServiceContainer::find(const std::string& srvName)
{
    impl::MicroServiceMap::iterator it = mImpl->mMicroSrvs.find(srvName);

    if (it != mImpl->mMicroSrvs.end())
    {
        return it->second;
    }

    return nullptr;
}


} /** namespace dog */
